fix(auth): pass WWW-Authenticate scopes to DCR registration request#705
Merged
DaleSeo merged 5 commits intomodelcontextprotocol:mainfrom Mar 3, 2026
Merged
fix(auth): pass WWW-Authenticate scopes to DCR registration request#705DaleSeo merged 5 commits intomodelcontextprotocol:mainfrom
DaleSeo merged 5 commits intomodelcontextprotocol:mainfrom
Conversation
When an MCP server returns a 401 with `WWW-Authenticate: Bearer scope="..."`, the scopes are parsed but never included in the Dynamic Client Registration (DCR) request. Per RFC 7591, the DCR request should include a `scope` field so the authorization server knows what scopes the client intends to use. Servers that enforce scope-matching between registration and authorization will reject the flow without this. Changes: - Add optional `scope` field to `ClientRegistrationRequest` with `skip_serializing_if` for backward compatibility - Update `register_client()` to accept scopes parameter and include them in the DCR request body and returned `OAuthClientConfig` - Thread scopes from `AuthorizationSession::new()` into both `register_client()` call sites - Re-export `oauth2::TokenResponse` trait so consumers can extract scopes from token responses - Add serialization tests for the new `scope` field
peschee
added a commit
to peschee/goose
that referenced
this pull request
Feb 27, 2026
…esponse Two fixes for OAuth scope handling in MCP server connections: 1. Patch rmcp to include scopes from WWW-Authenticate in the DCR registration request (see modelcontextprotocol/rust-sdk#705) 2. Extract granted_scopes from the token response instead of always saving an empty vec, so stored credentials accurately reflect what the authorization server granted.
4 tasks
peschee
added a commit
to peschee/goose
that referenced
this pull request
Feb 27, 2026
…esponse Two fixes for OAuth scope handling in MCP server connections: 1. Patch rmcp to include scopes from WWW-Authenticate in the DCR registration request (see modelcontextprotocol/rust-sdk#705) 2. Extract granted_scopes from the token response instead of always saving an empty vec, so stored credentials accurately reflect what the authorization server granted. Signed-off-by: Peter Siska <63866+peschee@users.noreply.github.com>
peschee
added a commit
to peschee/goose
that referenced
this pull request
Feb 27, 2026
…esponse Two fixes for OAuth scope handling in MCP server connections: 1. Patch rmcp to include scopes from WWW-Authenticate in the DCR registration request (see modelcontextprotocol/rust-sdk#705) 2. Extract granted_scopes from the token response instead of always saving an empty vec, so stored credentials accurately reflect what the authorization server granted. Signed-off-by: Peter Siska <63866+peschee@users.noreply.github.com>
peschee
added a commit
to peschee/goose
that referenced
this pull request
Feb 27, 2026
…esponse Two fixes for OAuth scope handling in MCP server connections: 1. Patch rmcp to include scopes from WWW-Authenticate in the DCR registration request (see modelcontextprotocol/rust-sdk#705) 2. Extract granted_scopes from the token response instead of always saving an empty vec, so stored credentials accurately reflect what the authorization server granted. Signed-off-by: Peter Siska <63866+peschee@users.noreply.github.com>
…[String] Avoids unnecessary Vec<String> allocation in callers that already have &[&str].
peschee
added a commit
to peschee/goose
that referenced
this pull request
Feb 28, 2026
…esponse Two fixes for OAuth scope handling in MCP server connections: 1. Patch rmcp to include scopes from WWW-Authenticate in the DCR registration request (see modelcontextprotocol/rust-sdk#705) 2. Extract granted_scopes from the token response instead of always saving an empty vec, so stored credentials accurately reflect what the authorization server granted. Signed-off-by: Peter Siska <63866+peschee@users.noreply.github.com>
Fix nightly rustfmt check by consolidating the separate
`use oauth2::TokenResponse` into the existing `use oauth2::{...}` block.
DaleSeo
approved these changes
Mar 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When an MCP server returns a
401withWWW-Authenticate: Bearer scope="read write", the scopes are correctly parsed and stored inAuthorizationManager.www_auth_scopes, and used for the authorization URL. However, they are never included in the Dynamic Client Registration (DCR) request.Per RFC 7591, the DCR request should include a
scopefield so the authorization server knows what scopes the client intends to use. Servers that enforce scope-matching between registration and authorization reject the flow without this.Changes
scopefield toClientRegistrationRequestwith#[serde(skip_serializing_if = "Option::is_none")]for backward compatibility — servers that don't expectscopein DCR won't see itregister_client()to accept ascopesparameter and include it in the DCR request body and returnedOAuthClientConfigAuthorizationSession::new()into bothregister_client()call sitesoauth2::TokenResponsetrait so downstream consumers (e.g., Goose) can call.scopes()on token responses to extract granted scopesscopefield is present whenSomeand absent whenNoneTest plan
cargo test -p rmcp --features auth --lib -- client_registration_request— both new tests passWWW-Authenticateand verify the DCR request includes thescopefield (inspect viaRUST_LOG=debug)scopein DCR continue to work (field omitted when no scopes)